﻿using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Data.Sqlite;

using System.Collections.Concurrent;

namespace LogNet6.Sqlite;
/// <summary>
/// Sqlite日志
/// </summary>
public class SqliteLoggerProcessService : ISqliteLoggerProcessor, ILoggerProcessor, IDisposable
{
    private const string TableName = "Logs";
    private readonly string _ConnectionString;

    private readonly BlockingCollection<LoggerPara> _MessageQueue = new();

    private readonly SqliteConnection _CN;
    private readonly SqliteCommand _Cmd;

    private readonly Thread _OutputThread;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="connectionString">Sql Server连接字符串</param>
    public SqliteLoggerProcessService(string connectionString)
    {
        _ConnectionString = connectionString;
        _CN = new SqliteConnection(connectionString);

        var cmd = _CN.CreateCommand();
        //DRop table {TableName};
        cmd.CommandText = $@"
CREATE TABLE if not exists {TableName} (
ID INTEGER  PRIMARY KEY autoincrement,
NAME     TEXT,
LogLevel TEXT,
Msg      TEXT,
Dt TEXT
);
";
        _CN.Open();
        cmd.ExecuteNonQuery();
        _CN.Close();




        _Cmd = _CN.CreateCommand();
        _Cmd.CommandText = $"INSERT INTO {TableName} (Dt,Name,LogLevel,Msg) VALUES (@Dt,@Name,@LogLevel,@Msg)";
        _Cmd.Parameters.Add(new SqliteParameter("@Name", SqliteType.Text));
        _Cmd.Parameters.Add(new SqliteParameter("@LogLevel", SqliteType.Text));
        _Cmd.Parameters.Add(new SqliteParameter("@Msg", SqliteType.Text));
        _Cmd.Parameters.Add(new SqliteParameter("@Dt", SqliteType.Text));

        // Start Console message queue processor
        _OutputThread = new Thread(ProcessLogQueue)
        {
            IsBackground = true,
            Name = "Logger queue processing thread"
        };
        _OutputThread.Start();
    }
    private void WriteMessage(LoggerPara p)
    {
        try
        {
            _Cmd.Parameters[0].Value = $"{p.CategoryName}[{p.EventId.Id}]";
            _Cmd.Parameters[1].Value = p.LogLevel.ToString();
            _Cmd.Parameters[2].Value = p.Message;
            _Cmd.Parameters[3].Value = p.DateTime;//.ToString("yyyy-MM-dd HH:mm:ss:fff");

            if (_CN.State == System.Data.ConnectionState.Closed)
                _CN.Open();
            _Cmd.ExecuteNonQuery();
        }
        catch (Exception)
        {
        }
    }
    private void ProcessLogQueue()
    {
        while (!_MessageQueue.IsCompleted)
        {
            try
            {
                WriteMessage(_MessageQueue.Take());
                if (_MessageQueue.Count == 0)
                    _CN.Close();
            }
            catch
            {
                break;
            }
        }
    }


    public void AddLog(LoggerPara message)
    {
        if (_MessageQueue.IsAddingCompleted)
            WriteMessage(message);
        else
            _MessageQueue.Add(message);

    }



    public void Dispose()
    {
        _MessageQueue.CompleteAdding();
        try
        {
            _OutputThread.Join(1500); // with timeout in-case Console is locked by user input
        }
        catch (ThreadStateException) { }

        _CN.Close();
    }

    public async Task<List<SqliteLoggerItem>> GetLogsAsync(int pageNo, int pageSize)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"SELECT Id,Name,LogLevel,Msg,Dt from {TableName} order by id desc limit {pageSize} offset {pageNo * pageSize}";
        await cn.OpenAsync();
        var dr = await cmd.ExecuteReaderAsync();
        List<SqliteLoggerItem> items = new();
        while (await dr.ReadAsync())
            items.Add(new SqliteLoggerItem(dr.GetInt32(0), dr.GetString(1), dr.GetString(2), dr.GetString(3), dr.GetDateTime(4)));
        await dr.CloseAsync();
        return items;

    }

    public List<SqliteLoggerItem> GetLogs(int pageNo, int pageSize)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"SELECT Id,Name,LogLevel,Msg,Dt from {TableName} order by id desc limit {pageSize} offset {pageNo * pageSize}";

        cn.Open();
        var dr = cmd.ExecuteReader();
        List<SqliteLoggerItem> items = new();
        while (dr.Read())
            items.Add(new SqliteLoggerItem(dr.GetInt32(0), dr.GetString(1), dr.GetString(2), dr.GetString(3), dr.GetDateTime(4)));
        dr.Close();
        return items;

    }

    public void Remove(int id)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"Delete from {TableName} where id={id}";
        cn.Open();
        cmd.ExecuteNonQuery();

    }

    public async Task RemoveAsync(int id)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"Delete from {TableName} where id={id}";
        await cn.OpenAsync();
        await cmd.ExecuteNonQueryAsync();
    }

    public void RemoveRange(int maxId)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"Delete from {TableName} where id<={maxId}";
        cn.Open();
        cmd.ExecuteNonQuery();
    }

    public async Task RemoveRangeAsync(int maxId)
    {
        using var cn = new SqliteConnection(_ConnectionString);
        var cmd = cn.CreateCommand();
        cmd.CommandText = $@"Delete from {TableName} where id={maxId}";
        await cn.OpenAsync();
        await cmd.ExecuteNonQueryAsync();
    }
}